package com.file.parser;

import com.file.bo.ResponseData;
import com.file.bo.SzSocialInsurance;
import com.file.bo.SzSocialInsuranceTran;
import com.file.constant.ErrorCode;
import com.file.util.JsonUtils;
import com.google.common.base.Splitter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import technology.tabula.*;
import technology.tabula.detectors.SpreadsheetDetectionAlgorithm;
import technology.tabula.extractors.SpreadsheetExtractionAlgorithm;

import java.io.File;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 深圳市社保pdf解析
 * @author v_wbhwliu
 */

@Slf4j
public class SzSocialInsurancePdfParser extends BasePdfParser {

    public ResponseData<String> parseSzSocialInsurancePdfToJson(String daId, String filePath) {
        log.info("parseSzSocialInsurancePdfToJson started, daId:{}", daId);
        String json = null;

        try {
            SzSocialInsurance socialInsurance = parseSocialInsurancePdf(filePath);
            json = JsonUtils.convertObjectToJson(socialInsurance);
        } catch (Exception e) {
            log.error("OnError|{}||{}|{}|{}|{}", ErrorCode.FILE_PARSE_EXCEPTION.getCode(), daId, "", "", "parseSzSocialInsurancePdfToJson failed", e);
            return new ResponseData<String>(json, ErrorCode.FILE_PARSE_EXCEPTION.getCode(), ErrorCode.FILE_PARSE_EXCEPTION.getMsg());
        }

        log.info("parseSzSocialInsurancePdfToJson completed, daId:{}", daId);
        return new ResponseData<String>(json, ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg());
    }

    public SzSocialInsurance parseSocialInsurancePdf(String filePath) {
        List<SzSocialInsuranceTran> szSocialInsuranceTrans = parseDetails(filePath);

        SzSocialInsurance szSocialInsurance = parseSocialInsuranceHeader(filePath);

        szSocialInsurance.setSocialInsuranceTranList(szSocialInsuranceTrans);

        return szSocialInsurance;
    }

    public SzSocialInsurance parseSocialInsuranceHeader(String filePath) {
        SzSocialInsurance szSocialInsurance = new SzSocialInsurance();
        String pdfHeaderText = getPdfTextByStripper2(filePath);

        String name = pdfHeaderText.substring(pdfHeaderText.indexOf("姓名：") + 3, pdfHeaderText.indexOf("社保电脑号")).trim();
        String computerNo = pdfHeaderText.substring(pdfHeaderText.indexOf("社保电脑号：") + 6, pdfHeaderText.indexOf("身份证号码")).trim();
        String idNo = pdfHeaderText.substring(pdfHeaderText.indexOf("身份证号码：") + 6, pdfHeaderText.indexOf("页码")).trim();
        String pageNo = pdfHeaderText.substring(pdfHeaderText.indexOf("页码：") + 3, pdfHeaderText.indexOf("最近参保单位名称")).trim();
        String participatingCompany = pdfHeaderText.substring(pdfHeaderText.indexOf("最近参保单位名称：") + 9, pdfHeaderText.indexOf("单位编号")).trim();
        String companyNo = pdfHeaderText.substring(pdfHeaderText.indexOf("单位编号：") + 5, pdfHeaderText.indexOf("计算单位")).trim();
        String calculateUnit = pdfHeaderText.substring(pdfHeaderText.indexOf("计算单位：") + 5, pdfHeaderText.indexOf("养老保险")).trim();

        String pensionPersonalAccountBalance = "";
        if (pdfHeaderText.indexOf("养老个人账户余额：") > 0) {//NOSONAR
            pensionPersonalAccountBalance = pdfHeaderText.substring(pdfHeaderText.indexOf("养老个人账户余额：") + 9,
                    pdfHeaderText.indexOf("其中：个人参保")).trim();
        }
        String medicalPersonalAccountBalanceStr = pdfHeaderText.substring(pdfHeaderText.indexOf("医疗个人账户余额：") + 9,
                pdfHeaderText.indexOf("单位编号对应的单位名称")).trim();
        String medicalPersonalAccountBalance = "";

        String separatorLine = System.getProperty("line.separator", "\n");
        if (medicalPersonalAccountBalanceStr.contains(separatorLine)) {
            List<String> list = Splitter.on(separatorLine).splitToList(medicalPersonalAccountBalanceStr);
            medicalPersonalAccountBalance = list.get(0);
        }

        String companyInfo = pdfHeaderText.substring(pdfHeaderText.indexOf("单位编号 单位名称") + 9,
                pdfHeaderText.indexOf("深圳市社会保险基金管理局")).trim();
        if (companyInfo.contains(separatorLine)) {
            companyInfo = companyInfo.replace(separatorLine, "");
        }

        List<SzSocialInsurance.InsuranceCompanyInfo> insuranceCompanyList = new ArrayList<>();

        Pattern pattern = Pattern.compile("\\d*\\s+");
        Matcher matcher = pattern.matcher(companyInfo);

        // 每行交易记录的第一个字所在的index
        int tranIndex = 0;
        Map<Integer, Integer> companyNoStartIndexMap = new HashMap<>();

        while (matcher.find()) {
            // log.info("Match \"" + matcher.group() + "\" at positions " + matcher.start() + "-" + (matcher.end() - 1));
            companyNoStartIndexMap.put(tranIndex, matcher.start());
            tranIndex++;
        }

        for (int i = 0; i < companyNoStartIndexMap.size(); i++) {
            SzSocialInsurance.InsuranceCompanyInfo insuranceCompanyInfo = new SzSocialInsurance.InsuranceCompanyInfo();
            if (i < companyNoStartIndexMap.size() - 1) {
                String companyNoAndName = companyInfo.substring(companyNoStartIndexMap.get(i), companyNoStartIndexMap.get(i + 1));
                String[] arr = companyNoAndName.split(StringUtils.SPACE);
                if (arr.length >= 2) {
                    insuranceCompanyInfo.setCompanyNo(arr[0]);
                    insuranceCompanyInfo.setCompanyName(arr[1].replaceAll("　", ""));//NOSONAR
                }
                insuranceCompanyList.add(insuranceCompanyInfo);
            } else {
                String lastCompanyNoAndNameText = companyInfo.substring(companyNoStartIndexMap.get(companyNoStartIndexMap.size() - 1));
                String[] arr = lastCompanyNoAndNameText.split(StringUtils.SPACE);
                if (arr.length >= 2) {
                    insuranceCompanyInfo.setCompanyNo(arr[0]);
                    insuranceCompanyInfo.setCompanyName(arr[1].trim());
                }
                insuranceCompanyList.add(insuranceCompanyInfo);
            }
        }

        szSocialInsurance.setName(name);
        szSocialInsurance.setComputerNo(computerNo);
        szSocialInsurance.setIdNo(idNo);
        szSocialInsurance.setPageNo(pageNo);
        szSocialInsurance.setParticipatingCompany(participatingCompany);
        szSocialInsurance.setCompanyNo(companyNo);
        szSocialInsurance.setCalculateUnit(calculateUnit);
        szSocialInsurance.setPensionPersonalAccountBalance(pensionPersonalAccountBalance);
        szSocialInsurance.setMedicalPersonalAccountBalance(medicalPersonalAccountBalance);
        szSocialInsurance.setInsuranceCompanyList(insuranceCompanyList);
        return szSocialInsurance;
    }

    public List<SzSocialInsuranceTran> parseDetails(String filePath) {
        List<SzSocialInsuranceTran> szSocialInsuranceTrans = new ArrayList<>();

        // 1. 读取文件
        File pdf = new File(filePath);

        // 2. pdfbox读取PDDocument
        try (PDDocument pdfDocument = PDDocument.load(pdf)) {

            // 3. tabula新建ObjectExtractor和NurminenDetectionAlgorithm，同时准备接收表格Rectangle的结构
            ObjectExtractor objectExtractor = new ObjectExtractor(pdfDocument);
//            NurminenDetectionAlgorithm detectionAlgorithm = new NurminenDetectionAlgorithm();
            SpreadsheetDetectionAlgorithm sea = new SpreadsheetDetectionAlgorithm();

            Map<Integer, List<Rectangle>> detectedTables = new HashMap<>();

            // 4. 获取每页的PageIterator
            PageIterator pages = objectExtractor.extract();

            // 5. 解析每页的Rectangle(table的位置)
            while (pages.hasNext()) {
                Page page = pages.next();

                List<Rectangle> tablesOnPage = sea.detect(page);
                if (tablesOnPage.size() > 0) {
                    detectedTables.put(page.getPageNumber(), tablesOnPage);
                }
            }

            // 6.通过table位置获取表格具体内容
            SpreadsheetExtractionAlgorithm bea = new SpreadsheetExtractionAlgorithm();

            // 如果单页pdf中有多个表格，则解析出来的List<Rectangle>会有多个对象
            for (Map.Entry<Integer, List<Rectangle>> entry : detectedTables.entrySet()) {
                Page page = objectExtractor.extract(entry.getKey());

                // 默认每页只有一个表格，因此获取第0个rectangle
                Rectangle rectangle = entry.getValue().get(0);
                Page area = page.getArea(rectangle.getTop(), rectangle.getLeft(), rectangle.getBottom(), rectangle.getRight());

                List<Table> table = bea.extract(area);

                // 默认每页只有一个表格，因此获取第0个table
                Table t = table.get(0);

                // 解析第一页时，去掉表头三行数据
                if (entry.getKey() == 1) {
                    for (int i = 0; i < t.getRowCount(); i++) {
                        if (Objects.nonNull(t.getCell(i, 0).getText())) {
                            if (t.getCell(i, 0).getText().contains("缴费年") ||
                                    t.getCell(i, 3).getText().contains("基数")) {
                                continue;
                            }
                            SzSocialInsuranceTran szSocialInsuranceTran = getDetailsByTable(t, i);
                            szSocialInsuranceTrans.add(szSocialInsuranceTran);
                        }
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return szSocialInsuranceTrans;
    }

    private SzSocialInsuranceTran getDetailsByTable(Table t, int i) {
        SzSocialInsuranceTran szSocialInsuranceTran = new SzSocialInsuranceTran();
        szSocialInsuranceTran.setPaymentYear(t.getCell(i, 0).getText(false));
        szSocialInsuranceTran.setPaymentMonth(t.getCell(i, 1).getText(false));
        szSocialInsuranceTran.setCompanyNo(t.getCell(i, 2).getText(false));
        SzSocialInsuranceTran.Pension pension = new SzSocialInsuranceTran.Pension();
        pension.setBase(t.getCell(i, 3).getText(false));
        pension.setCompanyPays(t.getCell(i, 4).getText(false));
        pension.setPersonalPays(t.getCell(i, 5).getText(false));
        szSocialInsuranceTran.setPension(pension);

        SzSocialInsuranceTran.MedicalInsurance medicalInsurance = new SzSocialInsuranceTran.MedicalInsurance();
        medicalInsurance.setType(t.getCell(i, 6).getText(false));
        medicalInsurance.setBase(t.getCell(i, 7).getText(false));
        medicalInsurance.setCompanyPays(t.getCell(i, 8).getText(false));
        medicalInsurance.setPersonalPays(t.getCell(i, 9).getText(false));
        szSocialInsuranceTran.setMedicalInsurance(medicalInsurance);

        SzSocialInsuranceTran.Fertility fertility = new SzSocialInsuranceTran.Fertility();
        fertility.setType(t.getCell(i, 10).getText(false));
        fertility.setBase(t.getCell(i, 11).getText(false));
        fertility.setCompanyPays(t.getCell(i, 12).getText(false));
        szSocialInsuranceTran.setFertility(fertility);

        SzSocialInsuranceTran.InjuryInsurance injuryInsurance = new SzSocialInsuranceTran.InjuryInsurance();
        injuryInsurance.setBase(t.getCell(i, 13).getText(false));
        injuryInsurance.setCompanyPays(t.getCell(i, 14).getText(false));
        szSocialInsuranceTran.setInjuryInsurance(injuryInsurance);

        SzSocialInsuranceTran.UnemploymentInsurance unemploymentInsurance = new SzSocialInsuranceTran.UnemploymentInsurance();
        unemploymentInsurance.setBase(t.getCell(i, 15).getText(false));
        unemploymentInsurance.setCompanyPays(t.getCell(i, 16).getText(false));
        unemploymentInsurance.setPersonalPays(t.getCell(i, 17).getText(false));
        szSocialInsuranceTran.setUnemploymentInsurance(unemploymentInsurance);

        return szSocialInsuranceTran;
    }


    public static void main(String[] args) {
        SzSocialInsurancePdfParser szSocialInsurancePdfParser = new SzSocialInsurancePdfParser();
//        SzSocialInsurance szSocialInsurance = szSocialInsurancePdfParser.parseSocialInsurancePdf("D:\\data\\files\\打印个人缴费清单.pdf");
//        SzSocialInsurance szSocialInsurance = szSocialInsurancePdfParser.parseSocialInsurancePdf("D:\\data\\files\\de1tuknz1547063374242889728_3bd57ae62e8cbec59faa661a2abc42e7_sipub_szsb.pdf");
        SzSocialInsurance szSocialInsurance = szSocialInsurancePdfParser.parseSocialInsurancePdf("D:\\data\\files\\dc2ojeob1544260707246391296_a96931578b56de0c1985e13ae6040ec8_sipub_szsb.pdf");

        String json = JsonUtils.convertObjectToJson(szSocialInsurance);
        System.out.println(json);
    }
}
